package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.apis.article.IArticleClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.SensitiveMapper;
import com.heima.wemedia.mapper.WmChannelsMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

    @Autowired
    WmNewsMapper wmNewsMapper;
    @Autowired
    GreenTextScan greenTextScan;
    @Autowired
    GreenImageScan greenImageScan;
    @Autowired
    FileStorageService fileStorageService;

    @Autowired
    IArticleClient iArticleClient;
    @Autowired
    WmChannelsMapper wmChannelMapper;
    @Autowired
    WmUserMapper wmUserMapper;
    @Autowired
    SensitiveMapper sensitiveMapper;





    @Override
    @Async//标注当前方法是异步方法  执行方法时,会新开启线程异步执行
    @Transactional
    public void autoScan(Integer id) {
        //文章审核  线程名字 task-1  文章id 6246
        log.info("文章审核  线程名字 {}  文章id {}", Thread.currentThread().getName(), id);
        //最好让审核线程休眠一会在执行审核逻辑
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //1.校验
        if(id==null){
          throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews wmNews = wmNewsMapper.selectById(id);
        if(wmNews==null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        //校验文章的状态是否为1
        if(wmNews.getStatus()==null || !wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }


        /**
         * {
         *     "content":"该文章所有的文本",
         *     "images":[xxx.jpg,xx.jpg]       "该文章去重之后所有的图片"
         * }
         */
        //文本和图片的抽取工作
        Map<String, Object> map = handlerTextAndImage(wmNews);

        //调用阿里云审核文本之前，先走自管理敏感词审核，如果审核失败，不用在调用阿里云了
        //自管理敏感词校验  返回false代表校验失败
        boolean textSensitiveScanRet = handlerTextSensitiveScan((String)map.get("content"), wmNews);
        if(!textSensitiveScanRet){
            log.info("自管理敏感词文本校验失败,文章标题或者内容中包含违禁词!");
            //throw new CustomException(AppHttpCodeEnum.ARTICLE_SCAN_FILE);
            return;
        }

        //2.调用阿里云审核文本(文章标题和内容拼接 掉一次阿里云即可) 如果阿里云返回block修改文章状态为2 如果返回pass则审核通过 如果返回review则修改为3
        //如果返回为false代表 校验失败
        boolean textScanRet = handlerTextScan((String)map.get("content"), wmNews);
        if(!textScanRet){
            log.info("文本校验失败,文章标题或者内容中包含违禁词!");
            //throw new CustomException(AppHttpCodeEnum.ARTICLE_SCAN_FILE);
            return;
        }


        //3.调用阿里云审核图片(包含封面图片及内容图片  去重之后调用) 如果阿里云返回block修改文章状态为2 如果返回pass则审核通过 如果返回review则修改为3
        boolean textImagesRet = handlerImageScan((List<String>)map.get("images"), wmNews);
        if(!textImagesRet){
            log.info("图片审核失败,文章中包含违禁图片!");
            //throw new CustomException(AppHttpCodeEnum.ARTICLE_SCAN_FILE);
            return;
        }


        //4.通过feign远程调用文章微服务 保存app端文章(ap_article ap_article_config ap_aritlce_content)
        ResponseResult responseResult = saveAppArticle(wmNews);
        log.info("通过feign远程调用文章微服务得到的结果: {}", JSON.toJSONString(responseResult));
        if(responseResult==null){
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        if(responseResult.getCode()==null || !responseResult.getCode().equals(200)){
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }


        //5.修改文章的发布状态为9(已发布)  同时修改article_id字段
        String articleId = (String)responseResult.getData();
        //wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
        wmNews.setArticleId(Long.valueOf(articleId));
        updateWmNews(wmNews, 9, "审核成功");
    }


    //项目启动  就执行
    @PostConstruct// 该注解作用时间，是当前类实例化之后，属性注入之后
    public void initSensitive(){
        log.info("初始化敏感词到map结构中");
        List<WmSensitive> wmSensitives = sensitiveMapper.selectList(null);//查询所有敏感词
//        List<String> list = new ArrayList<>();
//        for (WmSensitive wmSensitive : wmSensitives) {
//            list.add(wmSensitive.getSensitives());
//        }
        List<String> list = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
        SensitiveWordUtil.initMap(list);
    }
    /**
     * 自管理敏感词校验
     * @param content 所有的文本(包含标题和内容)
     * @param wmNews
     * @return
     */
    private boolean handlerTextSensitiveScan(String content, WmNews wmNews) {
        Map<String, Integer> matchWords = SensitiveWordUtil.matchWords(content);
        if(matchWords!=null && matchWords.size()>0){
            //说明文章文本中包含违禁词
            //修改文章状态为2
            updateWmNews(wmNews, 2, "文章中存在违禁图片,限制公开");
            return false;
        }
        return true;//自管理敏感词审核通过
    }

    /**
     * 远程调用article微服务   保存app端文章(ap_article ap_article_config ap_aritlce_content)
     * @param wmNews
     * @return
     */
    private ResponseResult saveAppArticle(WmNews wmNews) {
        ArticleDto dto = new ArticleDto();
        //属性的拷贝
        BeanUtils.copyProperties(wmNews, dto);
        //文章的布局
        dto.setLayout(wmNews.getType());
        //频道
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        if (wmChannel != null) {
            dto.setChannelName(wmChannel.getName());
        }
        //作者
        dto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null) {
            dto.setAuthorName(wmUser.getName());
        }
        //设置文章id
        if (wmNews.getArticleId() != null) {
            dto.setId(wmNews.getArticleId());
        }else{
            dto.setId(null);
        }
        dto.setCreatedTime(new Date());

        ResponseResult responseResult = iArticleClient.saveArticle(dto);
        return responseResult;
    }

    @Autowired
    Tess4jClient tess4jClient;

    /**
     * 调用阿里云审核图片
     * @param images  文章中所有的图片(封面和内容)     http://192.168.200.130:9000/leadnews/2021/4/20210418/7d0911a41a3745efa8509a87f234813c.jpg
     * @param wmNews
     * @return false代表审核图片失败
     */
    private boolean handlerImageScan(List<String> images, WmNews wmNews) {
        if(images==null || images.size()==0){
            //说明文章没有图片
            return true;//代表不用审核图片    此处不能抛异常, 也不能return false
        }
        List<byte[]> imagesList = new ArrayList<>();
        try {
            //将图片路径  转换为  字节数组
            for (String image : images) {
                byte[] bytes = fileStorageService.downLoadFile(image);




                //使用OCR技术 识别图片中的文本    将图片中识别出来文本  进行自管理敏感词审核
                //图片识别文字审核---begin-----
                //从byte[]转换为butteredImage
                ByteArrayInputStream in = new ByteArrayInputStream(bytes);
                BufferedImage imageFile = ImageIO.read(in);
                //识别图片的文字
                String result = tess4jClient.doOCR(imageFile);

                //审核是否包含自管理的敏感词
                boolean isSensitive = handlerTextSensitiveScan(result, wmNews);
                if(!isSensitive){
                    return isSensitive;
                }

                //图片识别文字审核---end-----





                imagesList.add(bytes);
            }
        } catch (Exception exception) {
            log.error("调用阿里云审核图片时,从minio下载图片失败", exception);
            //修改文章状态为3
            updateWmNews(wmNews, 3, "文章中存在不确定内容,需要人工处理");
            return false;
        }

        try {
            Map map = greenImageScan.imageScan(imagesList);
            if(map!=null){
                String suggestion = (String)map.get("suggestion");
                if("block".equals(suggestion)){
                    //修改文章状态为2
                    updateWmNews(wmNews, 2, "文章中存在违禁图片,限制公开");
                    return false;
                }else if("review".equals(suggestion)){
                    //修改文章状态为3
                    updateWmNews(wmNews, 3, "文章中存在不确定图片,需要人工处理");
                    return false;
                }else{
                    return true;//文本审核通过
                }
            }
        } catch (Exception exception) {
            log.error("调用阿里云审核图片失败", exception);
        }
        //修改文章状态为3
        updateWmNews(wmNews, 3, "调用阿里云失败,需要人工审核");
        return false;
    }

    /**
     * 抽取文章所有的文本和图片
     * @param wmNews
     * @return
     */
    private Map<String, Object> handlerTextAndImage(WmNews wmNews) {
        StringBuilder sbd = new StringBuilder();//代表该文章所有的文本
        Set<String> urls = new HashSet<>();//该集合代表 该文章去重之后的图片路径集合

        if(StringUtils.isNotBlank(wmNews.getContent())){
            List<Map> list = JSON.parseArray(wmNews.getContent(), Map.class);
            for (Map map : list) {
                if(map.get("type").equals("text")){
                    sbd.append((String)map.get("value"));
                }
                if(map.get("type").equals("image")){
                    urls.add((String)map.get("value"));
                }
            }
        }
        //文章标题处理
        if(StringUtils.isNotBlank(wmNews.getTitle())){
            sbd.append(wmNews.getTitle());
        }
        //处理文章封面图片     xxxxx.jpg,xxx.jpg
        if(StringUtils.isNotBlank(wmNews.getImages())){
            String[] split = wmNews.getImages().split(",");
            urls.addAll(Arrays.asList(split));
        }
        Map<String,Object> map = new HashMap<>();
        map.put("content", sbd.toString());
        map.put("images", new ArrayList<>(urls));
        return map;
    }

    /**
     * 调用阿里云校验文本
     * @param content  文章中所有的文本(包含标题和内容)
     * @param wmNews
     * @return  false代表审核失败
     */
    private boolean handlerTextScan(String content, WmNews wmNews) {
        try {
            //{suggestion=block, label=contraband}
            Map map = greenTextScan.greeTextScan(content);
            if(map!=null){
                String suggestion = (String)map.get("suggestion");
                if("block".equals(suggestion)){
                    //修改文章状态为2
                    updateWmNews(wmNews, 2, "文章中存在违禁词,限制公开");
                    return false;
                }else if("review".equals(suggestion)){
                    //修改文章状态为3
                    updateWmNews(wmNews, 3, "文章中存在不确定内容,需要人工处理");
                    return false;
                }else{
                    return true;//文本审核通过
                }
            }
        } catch (Exception exception) {
            log.error("调用阿里云审核文本失败", exception);
        }
        //修改文章状态为3
        updateWmNews(wmNews, 3, "调用阿里云失败,需要人工审核");
        return false;
    }

    /**
     * 修改文章状态和审核原因
     * @param wmNews
     * @param status
     * @param reason
     */
    private void updateWmNews(WmNews wmNews, int status, String reason) {
        wmNews.setStatus((short)status);
        wmNews.setReason(reason);
        wmNewsMapper.updateById(wmNews);
    }


}
